package com.technodo.gamedata;


import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
import java.util.SortedSet;
import java.util.TreeSet;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.graphics.Color;
import android.util.Log;

import com.technodo.avatar.PlayerProfile;
import com.technodo.utils.JsonUtils;
import com.technodo.utils.TechnodoColor;

public class GameRules {
	private String TAG = "GameRules";
	/**
	 * Round of the game 
	 */
	private int round;

	/**
	 * index of the player who has the next turn
	 */
	private int nextTurn;
	/**
	 * index of the current player
	 */
	private int currentPlayerIndex;
	/**
	 * Maintain the round wise turn order
	 */
	private int[] turnOrder;
	/**
	 * Number of players playing the game
	 */
	private int numPlayers;
	/**
	 * advertise row(left)
	 */
	private int advertisedRows;
	/**
	 * subsidy column(up)
	 */
	private int subsidy;
	/**
	 * Player Board for each player playing the game
	 */
	private PlayerBoard[] playerBoard;
	/**
	 * Map of All Techs
	 */
	private HashMap <String, Tech> techMap;

	/**
	 * List of available Techs
	 */
	private SortedSet <Tech> availableTechs;
	/**
	 * Map of All Products
	 */
	private HashMap <String, Product> productMap;
	/**
	 * Placing of the products in the slots
	 * NULL means there are no products in the slot
	 * This is related to the first products being shown 
	 */
	private Product[][] frontProductPlacing;
	/**
	 * Placing of the products in the slots
	 * NULL means there are no products in the slot
	 * This is related to the back row of products being placed
	 */
	private Product[][] backProductPlacing;
	/**
	 * Map of All Directors
	 */
	private HashMap<String, Director> directorMap;
	/**
	 * Queue of available Directors 
	 */
	private Queue<Director> availableDirectors;

	//////////////////////////////////////////////////////////////////////////////////////////
	// Transient gaming variables
	/**
	 * Date representing the year month etc
	 */
	private Date turnDate;
	/**
	 * Default Constructor
	 */
	public GameRules() {
		super();
	}
	/**
	 * Constructor for GameRules (used during first load of this particular level)
	 * 
	 * @param directorPath Path of directors json
	 * @param productPath  Path of Products json
	 * @param techPath	   Path of Technology json
	 * 
	 * 1. Parse the Director Json to form the hashmap
	 * 2. Parse the Product  Json to form the hashmap
	 * 3. Parse the Tech     Json to form the hashmap
	 * 4. Call init function to form the randomised versions of stack for director, product, tech
	 * 5. Initialise various gaming variables 
	 */
	public GameRules(String directorPath, String productPath, String techPath) 
			throws JSONException, IllegalStateException {

		// 1. 2. 3. Parsing of all the directors etc is over.
		// Parse Map Tiles to setup data structure
		ParseTechCards(techPath);	
		ParseProductCards(productPath);
		parseDirectorCards(directorPath);
	}
	/**
	 * Resets the game variables.
	 *  
	 * @param startDate Date corresponding to first turn in the game
	 * 
	 *  To be called before starting the game
	 */
	public boolean resetGame(Date startDate) throws IllegalStateException {
		Log.d(TAG,"in resetGame");
		// Initialise local game transient variables
		numPlayers = 0;
		round = 1;
		advertisedRows = 0;
		subsidy = 0;
		setTurnDate(startDate);
		turnOrder = new int[5];
		playerBoard = new PlayerBoard[4];
		frontProductPlacing = new Product[4][4];
		backProductPlacing = new Product[4][4];
		return true;
	}
	/**
	 * 
	 * @return returns the sorted set of all the techs that are available 
	 */
	public SortedSet<Tech> getAvailableTechs(){
		return availableTechs;
	}
	/**
	 * 
	 * @return returns the number of techs that are available to the player
	 */
	public int getNumberOfAvailableTechs(){
		Log.d(TAG,"in getavailableTechs :" +availableTechs.size());
		return availableTechs.size();
	}
	/**
	 * Add Players to the game
	 * 
	 * @param player Player to be added
	 * @param me     Whether the player is the main user of the mobile
	 */
	public boolean addPlayer(PlayerProfile player, boolean me) {

		if(playerBoard == null) {
			playerBoard = new PlayerBoard[4];
		}
		playerBoard[numPlayers] = new PlayerBoard(player, me, numPlayers);		

		numPlayers = numPlayers + 1;
		Log.d(TAG,"player added in addplayer "+ numPlayers);
		return true;
	}
	/**
	 * 
	 * @return number of players playing the current game 
	 */
	public int getNumberOfPlayingPlayers(){
		return numPlayers;
	}
	/**
	 * 
	 * @param playerIndex the index of the player (who is playing the game) and has to be added to the turn order
	 */
	public void addTurn(int playerIndex){
		turnOrder[numPlayers] = playerIndex;
	}
	/**
	 * 
	 * @param CPI : current player index
	 * @return : value of the player index w.r.t to the playerboard[]
	 */
	public int getNextTurn(int CPI){
		return turnOrder[CPI];
	}
	/**
	 * Initialise rules and state for this game
	 * 
	 * 1. Go through Directors, Products and Tech and assemble them
	 * 
	 * @return
	 * @throws IllegalStateException
	 */
	public boolean initRules() throws IllegalStateException {

		Log.d(TAG, "Entering initRules");

		// Fill in the tech list
		if(techMap == null)
			throw new IllegalStateException("Tech Map is Null, Parsing error maybe. Do Master reset");
		availableTechs = new TreeSet<Tech>(new Comparator<Tech>() 
				{

			public int compare(Tech lhs, Tech rhs) {
				int color = lhs.getColor().getValue() - rhs.getColor().getValue();
				if(color != 0) 
					return color;
				int strdiff = lhs.getName().compareTo(rhs.getName());
				return strdiff;
			}
				});

		for(Object value : techMap.values()) {
			availableTechs.add((Tech)value);
		}		

		Log.d(TAG,"number of available Techs are :" +availableTechs.size());
		// Fill in the products
		if(productMap == null)
			throw new IllegalStateException("Tech Map is Null, Parsing error maybe. Do Master reset");		
		randomiseProductLocation();

		// Fill in the director list
		//		if(directorMap == null) 
		//			throw new IllegalStateException("Director Map is Null, Parsing error maybe. Do Master reset");
		//		availableDirectors = new LinkedList<Director>();
		//		
		//		// Move all values from the hashmap to the stack of directors
		//		// FIXME: Randomise pushing to directors to the Queue
		//		for(Object value : directorMap.values()) {
		//			availableDirectors.add((Director) value);
		//		}
		return true;
	}
	////////////////////////////////////////////////////////////////////////////////////////////////////
	// Private helper functions

	/**
	 * Parse tech cards and place them into the Game Rules hashmap
	 * 
	 * @throws JSONException, IllegalStateException 
	 * 
	 */
	private void ParseTechCards(String filename) throws JSONException, IllegalStateException {

		Log.i(TAG, "Parsing Tech Cards : " + filename);

		String json = JsonUtils.GetJsonFromFile(filename);
		if ((json == null) || (json.length() == 0)) {
			throw new IllegalStateException("Json tech config file blank " + filename);
		}

		// Create a Jobject from the json
		// May throw json exception in case of malformed json
		JSONObject jObject = new JSONObject(json);
		JSONArray  jsonTechs = jObject.getJSONArray("Cards");

		// Create hashmap for Techs
		techMap = new HashMap<String, Tech>();

		for(int i = 0; i < jsonTechs.length(); i++) {
			// Create a new tech
			Tech tech = new Tech((JSONObject) jsonTechs.getJSONObject(i));
			techMap.put(tech.getName(), tech);
			//			Log.i(TAG, "Parsed Tech  (" + i + ") : " + tech.toString());

		}

		Log.i(TAG, "Parsed Tech Cards : " + filename);

	}
	/**
	 * Parse product cards and place them into the Game Rules hashmap
	 * 
	 * @throws JSONException, IllegalStateException 
	 * 
	 */
	private void ParseProductCards(String filename) throws JSONException, IllegalStateException {

		Log.i(TAG, "Parsing Product Cards : " + filename);

		String json = JsonUtils.GetJsonFromFile(filename);
		if ((json == null) || (json.length() == 0)) {
			throw new IllegalStateException("Json Product config file blank " + filename);
		}

		// Create a Jobject from the json
		// May throw json exception in case of malformed json
		JSONObject jObject = new JSONObject(json);
		JSONArray  jsonProduct = jObject.getJSONArray("Cards");

		// Create hashmap for Techs
		productMap = new HashMap<String, Product>();

		for(int i = 0; i < jsonProduct.length(); i++) {
			// Create a new Product
			Product product = new Product((JSONObject) jsonProduct.getJSONObject(i));
			productMap.put(product.getName(), product);
			//			Log.i(TAG, "Parsed Product  (" + i + ") : " + product.toString());

		}

		Log.i(TAG, "Parsed Product Cards : " + filename);
	}

	/**
	 * Parse director cards and place them into the Game Rules hashmap
	 * 
	 * @throws JSONException, IllegalStateException 
	 * 
	 */
	private void parseDirectorCards(String filename) throws JSONException {
		Log.i(TAG, "Parsing Director Cards : " + filename);

		String json = JsonUtils.GetJsonFromFile(filename);
		if ((json == null) || (json.length() == 0)) {
			throw new IllegalStateException("Json director config file blank " + filename);
		}

		// Create a JSONobject from the json
		// May throw json exception in case of malformed json
		JSONObject jObject = new JSONObject(json);
		JSONArray  jsonDirector = jObject.getJSONArray("Cards");

		// Create hashmap for Directors
		directorMap = new HashMap<String, Director>();

		ArrayList<Director> directorList = new ArrayList<Director>();

		for(int i = 0; i < jsonDirector.length(); i++) {
			// Create a new Director
			Director director = new Director((JSONObject) jsonDirector.getJSONObject(i));
			directorMap.put(director.getName(), director);
			directorList.add(director);
			//			Log.i(TAG, "Parsed Director  (" + i + ") : " + director.toString());

		}

		Random random = new Random(System.currentTimeMillis());
		availableDirectors = new LinkedList<Director>();
		for(int i = 0; i < directorList.size(); ) {

			int randomValue = randomiser(random, directorList.size());
			if(directorList.get(randomValue) != null) {
				// adding a random director into the queue
				availableDirectors.add(directorList.get(randomValue));
				directorList.set(randomValue, null);
				i++;
			}			
		}

		Log.i(TAG, "Parsed Director Cards : " + filename);

	}
	/////////////////////////////////////////////////////////////////////////////////////////////////////
	// Game related actions
	/**
	 * Buy tech action.
	 * 
	 * @param board
	 * @param tech
	 * 
	 * Should be called on confirmation that the user wishes to buy the tech. 
	 */
	public void ActionBuyTech(PlayerBoard board, Tech tech) {
		// If already owned cannot rebuy tech
		if(tech.getPlayerBoard() != null) {
			throw new IllegalStateException("Tech is no patentable by Player" + tech.toString() + board.toString());
		}
		Log.d(TAG,"value of tech to be bought is  "+tech.getName());
		// Buy the tech and remove from pool of free techs
		board.BuyTech(tech);
		// No longer removing techs, having a playerboard associated marks the tech as unavailable
		// availableTechs.remove(tech);

		// Use a move left
		board.useMoves();
	}	
	/**
	 * Buy engg
	 * 
	 * @param board Player who is buying engg
	 * 
	 * 1a. Check, if engg is buyable, or throw IllegalStateException
	 * 1b. Check, if engg level is available for increment (not max level), or throw IllegalStateException
	 * 2. Decrement Cost of Engg from number of gold
	 * 3. Increment engg if the board
	 * 4. Use up a move
	 */
	public void ActionBuyEngg(PlayerBoard board) {
		// Buy engg
		board.BuyEngg();

		// Use moves
		board.useMoves();
	}
	/**
	 * Buy a factory
	 * 
	 * @param board Player who is buying the factory
	 * 
	 * 1a. Check, if factory is buyable, or throw IllegalStateException
	 * 1b. Check, if factory level is available for increment (not max level), or throw IllegalStateException
	 * 2. Decrement Cost of Factory from number of gold
	 * 3. Increment Factory if the board
	 * 4. Use up a move
	 */
	public void ActionBuyFactory(PlayerBoard board) {

		// Buy factory.
		board.BuyFactory();

		// Use moves
		board.useMoves();
	}
	/**
	 * Buy a director
	 * 
	 * @param board Player who is buying the Director
	 * 
	 * 1a. Check, if director is buyable, or throw IllegalStateException
	 * 1b. Check, if director is available , or throw IllegalStateException
	 * 2. Decrement Cost of Director
	 * 3. Increment Director to the board
	 * 4. Use up a move
	 */
	public void ActionBuyDirector(PlayerBoard board, Director director) {

		// Buy the director
		board.BuyDirector(director);
		// Use moves
		board.useMoves();
	}
	/**
	 * Action Advertise
	 * 
	 * @param board Player who is advertising
	 * @param advertisedRow row which is to be advertised
	 * @return 
	 * 
	 * 1a. Check, if advertising is buyable, or throw IllegalStateException 
	 * 2. Move advertising marker to requisite row
	 * 3. Decrement cost from the board
	 * 4. Use up a move
	 */
	public boolean ActionAdvertise(PlayerBoard board, int advertisedRow) {

		int cost = 0;
		boolean buyFlag = false;
		
		if((advertisedRows - advertisedRow) == 0)
			cost = 0;
		else if((advertisedRows - advertisedRow) == 1 || (advertisedRows - advertisedRow) == -1 )
			cost = 1;
		else if((advertisedRows - advertisedRow) == 2 || (advertisedRows - advertisedRow) == -2)
			cost = 4;
		else
			cost = 1;
		
		// Check, if advertising is buyable
		if(IsAdvertisingBuyable(board, cost)) {

			buyFlag = true;
			
			// decrease the gold count for the player
			board.spendGold(cost);

			// Move advertising marker to requisite row
			advertisedRows = advertisedRow;

			// Use moves
			board.useMoves();
		}
		
		return buyFlag;
	}
	
	
	public boolean IsAdvertisingBuyable(PlayerBoard board, int cost) {
		boolean flag = false;
		if(board.getGold() >= cost)
			flag = true;
					
		return flag;
	}
	/**
	 * Action Cost Cut
	 * 
	 * @param board Player who is cost cutting
	 * @param brandValueLost Amount of brand value being forfeited by the player
	 * 
	 * 1a. No checks reqd, user can cost cut anytime 
	 * 2. Decrement Brand Value from the board (as per request)
	 * 3. Increment gold of the board
	 * 4. Use up a move
	 */
	public void ActionCostCut(PlayerBoard board, int brandValueLost) {

		if(board.getBrandValue()-brandValueLost >0)
		{
			board.setBrandValue(board.getBrandValue()-brandValueLost);
			board.earnGold(brandValueLost);
		}
		else throw new IllegalStateException("Not enough brand value");

		// Use moves
		board.useMoves();
	}
	/**
	 * Build a particular product to earn and distribute brand value
	 * 
	 * @param product 		Product to be build
	 * @param board			Player Building the product
	 * @param tech1Owner	Owner of Tech1 - {@link PlayerBoard}
	 * @param tech2Owner 	Owner of Tech2 - {@link PlayerBoard}
	 * @param enggOwner 	Owner of Engg  - {@link PlayerBoard}
	 * @param factoryOwner  Owner of Factory - {@link PlayerBoard}
	 * @param row			
	 * @param column
	 * 
	 * 1. Check if all techs, engg and factory are owned. - throw IllegalStateException
	 * 2. Check if all techs, engg and factory are owned by owners as supplied in params - throw IllegalStateException 
	 * 3. Calculate Profit, BrandValue from Product
	 * 4. Distribute Profit, BrandValue from Product to different owners, round up in favour of Product creator.
	 * 5. Use up a turn
	 */
	public void ActionProductise(Product product, PlayerBoard board, PlayerBoard tech1Owner, PlayerBoard tech2Owner, PlayerBoard enggOwner,
			PlayerBoard factoryOwner, Integer row, Integer column) {

		// null check for playerboards
		if(product == null || board == null || tech1Owner == null || tech2Owner == null ||
				enggOwner == null || factoryOwner == null || row == null || column == null)
			throw new IllegalStateException("Product is not buildable");

		// Check if all techs, engg and factory are owned by owners as supplied in params
		if(!tech1Owner.getTechCards().contains(getTech(product.getTech1name())))
			throw new IllegalStateException("First technology mentioned on the product is not available on the playerboard");

		if(!tech2Owner.getTechCards().contains(getTech(product.getTech2name())))
			throw new IllegalStateException("Second technology mentioned on the product is not available on the playerboard");

		if(!(product.getEngg() <= enggOwner.getEngg()))
			throw new IllegalStateException("required engineering level" + product.getEngg() + "is not available on playerboard");

		if(!(product.getFactory() <= factoryOwner.getFactory()))
			throw new IllegalStateException("required factory level" + product.getFactory() + "is not available on playerboard");

		// calculating profit and brandvalue from product
		int profit = product.getProfit();
		int bv = product.getBrandValue();
		if(advertisedRows == row) {
			profit += profit * 2;
			bv = bv * 2;
		}
		if(subsidy == column)
			profit += profit * 2;

		// adding product to the list of products in playerboard
		board.getProducts().add(product);

		// increase the brandValue and set it to board's brandValue
		int brandValue = board.getBrandValue();				
		brandValue += bv;
		board.setBrandValue(brandValue);

		int divGold = profit/4;
		int remainderGold = profit % 4;

		// distributing profit from Product among different owners
		tech1Owner.earnGold(divGold);
		tech2Owner.earnGold(divGold);
		enggOwner.earnGold(divGold);
		factoryOwner.earnGold(divGold);
		board.earnGold(remainderGold);

		// Use moves
		board.useMoves();
	}
	/**
	 * For each product, finds a slot to place it in.
	 * Random number seed taken based on time
	 */
	private void randomiseProductLocation() {
		// Seed the random number with time such that Randomness is preserved
		long time = System.currentTimeMillis();
		Random random = new Random(time);
		Log.i(TAG, "Random Generator Seed = " + time);

		for(Object value : productMap.values()) {
			boolean placedObject = false;
			while(placedObject == false) {
				int front = randomiser(random, 2);
				int row   = randomiser(random, 4);
				int col   = randomiser(random, 4);

				if(front == 1) {
					if(frontProductPlacing[row][col] == null) {
						placedObject = true;
						frontProductPlacing[row][col] = (Product) value;
						Log.i(TAG, "Placed Product " + ((Product)value).getName() + " in front at [" + row + "][" + col + "]");
					}					
				} else { // front == 0
					if(backProductPlacing[row][col] == null) {
						placedObject = true;
						backProductPlacing[row][col] = (Product) value;
						Log.i(TAG, "Placed Product " + ((Product)value).getName() + " in back at [" + row + "][" + col + "]");
					}
				}
				Log.d(TAG, "Attempt to Place Product " + ((Product)value).getName() 
						+ "[" + front + "][" + row + "][" + col + "]" + " result : " + placedObject);

			}
		}

	}

	private int randomiser(Random random, int max) {
		return random.nextInt(max);
	}

	public Date getTurnDate() {
		return turnDate;
	}

	public void setTurnDate(Date turnDate) {
		this.turnDate = turnDate;
	}
	/**
	 * @return the techMap
	 */
	public HashMap<String, Tech> getTechMap() {
		return techMap;
	}

	public int getCurrentPlayerIndex() {
		return currentPlayerIndex;
	}

	public void setCurrentPlayerIndex(int currentPlayer) {
		this.currentPlayerIndex = currentPlayer;
	}
	public void updateCurrentPlayerIndex(){
		Log.d(TAG,"updateCurrentPlayerIndex() called");
		// If moves left is 0, then move to the next player
		if(getCurrentPlayerBoard().getMovesLeft() == 0)
		{
			//			Log.d(TAG,"inside if updateCurrentPlayerIndex()");
			calculateNextPlayer();
		}
	}
	public Tech getTech(String techName){
		return techMap.get(techName);
	}

	/**
	 * @return the advertisedRows
	 */
	public int getAdvertisedRows() {
		return advertisedRows;
	}
	/**
	 * @param advertisedRows the advertisedRows to set
	 */
	public void setAdvertisedRows(int advertisedRows) {
		this.advertisedRows = advertisedRows;
	}
	/**
	 * @return the subsidy
	 */
	public int getSubsidy() {
		return subsidy;
	}
	/**
	 * @param subsidy the subsidy to set
	 */
	public void setSubsidy(int subsidy) {
		this.subsidy = subsidy;
	}

	/**
	 * Is the product buyable?
	 * 
	 * @param tech1 Technology 1 associated with the product
	 * @param tech2 Technology 2 associated with the product
	 * @param engg	Engg level associated with the product
	 * @param factory Factory level associated with the product
	 * @return
	 * 
	 * Check if the tech1 and tech2 associated with the product
	 * are available or not.
	 * Check if the engg level and factory level required for product are available or not
	 */
	public boolean IsProductBuyable(Tech tech1, Tech tech2, Integer engg, Integer factory) {
		Log.d("TAG,","buying product engg "+engg + "factory "+factory);
		boolean flag = false;
		int iTmp = 0;
		int jTmp = 0;

		if(tech1.getPlayerBoard() != null && tech2.getPlayerBoard() != null) {
			Log.d(TAG,"buying product in if part");

			for(int i = 0; i < getNumberOfPlayingPlayers(); i++) {
				if(getCurrentPlayerBoard(i).getEngg() >= engg) {
					iTmp++;
				}
				if(getCurrentPlayerBoard(i).getFactory() >= factory) {
					jTmp++;
				}
				if(iTmp >= 1 && jTmp >= 1) {
					flag = true;
					break;
				}
			}
		}
		return flag;
	}
	/**
	 * 
	 * @param enggLevel
	 * @return list of player boards who have engg greater than enggLevel 
	 */
	public ArrayList<PlayerBoard> whoHasEngg(Integer enggLevel) {

		ArrayList<PlayerBoard> enggBoards = new ArrayList<PlayerBoard>();
		for(int i = 0; i < getNumberOfPlayingPlayers(); i++) {
			if(getCurrentPlayerBoard(i).getEngg() >= enggLevel)
				enggBoards.add(getCurrentPlayerBoard(i));
		}
		return enggBoards;
	}

	/**
	 * 
	 * @param factoryLevel
	 * @return list of player boards who have factory greater than factoryLevel
	 */
	public ArrayList<PlayerBoard> whoHasFactory(Integer factoryLevel) {

		ArrayList<PlayerBoard> factoryBoards = new ArrayList<PlayerBoard>();
		for(int i = 0; i < getNumberOfPlayingPlayers(); i++) {
			if(getCurrentPlayerBoard(i).getFactory() >= factoryLevel)
				factoryBoards.add(getCurrentPlayerBoard(i));
		}
		return factoryBoards;
	}

	/**
	 * Calculates the brand value of a particular director
	 * 
	 * @param director
	 * @param playerBoard
	 * @return brandValue
	 */
	public int brandValueOfDirector(Director director) {

		int brandValue = 0;
		int count = 0;
		if(director != null) {
			String qualifier = director.getQualifier();
			int multiplier = director.getMultiplier();
			int divider = director.getDivider();

			int numPlayers = getNumberOfPlayingPlayers();
			ArrayList<Tech> techList = (ArrayList<Tech>) getCurrentPlayerBoard().getTechCards();
			ArrayList<Product> productsList = (ArrayList<Product>) getCurrentPlayerBoard().getProducts();

			if(qualifier.equalsIgnoreCase("tech")) {				
				for(int i = 0; i<techList.size(); i++) {
					if(director.getColor() == techList.get(i).getColor())
						count++;
				}
				brandValue = (multiplier*numPlayers*count)/divider;

			} else if(qualifier.equalsIgnoreCase("factory")) {
				brandValue = (multiplier*numPlayers*getCurrentPlayerBoard().getFactory())/divider;

			} else if(qualifier.equalsIgnoreCase("engg")) {
				brandValue = (multiplier*numPlayers*getCurrentPlayerBoard().getEngg())/divider;

			} else if(qualifier.equalsIgnoreCase("product_tech")) {
				for(int i = 0; i<productsList.size(); i++) {
					String tech1 = productsList.get(i).getTech1name();
					String tech2 = productsList.get(i).getTech2name();
					if(director.getColor() == getTechMap().get(tech1).getColor() 
							|| director.getColor() == getTechMap().get(tech2).getColor()) {

						count++;						
					}
				}
				brandValue = (multiplier*numPlayers*count)/divider;

			} else if(qualifier.equalsIgnoreCase("product_factory")) {
				for(int i = 0; i<productsList.size(); i++) {
					count += productsList.get(i).getFactory();
				}
				brandValue = (multiplier*numPlayers*count)/divider;

			} else if(qualifier.equalsIgnoreCase("product_engg")) {
				for(int i = 0; i<productsList.size(); i++) {
					count += productsList.get(i).getEngg();
				}
				brandValue = (multiplier*numPlayers*count)/divider;

			} else if(qualifier.equalsIgnoreCase("product_color")) {
				for(int i = 0; i<productsList.size(); i++) {
					String tech1 = productsList.get(i).getTech1name();
					String tech2 = productsList.get(i).getTech2name();
					if(director.getColor() == getTechMap().get(tech1).getColor() 
							&& director.getColor() == getTechMap().get(tech2).getColor()) {

						count++;						
					}
				}
				brandValue = (multiplier*numPlayers*count)/divider;

			} else if(qualifier.equalsIgnoreCase("tech_set")) {
				count = calculate(techList);
				brandValue = (multiplier*numPlayers*count)/divider;

			} else if(qualifier.equalsIgnoreCase("own_engg_factory")) {
				for(int i = 0; i<productsList.size(); i++) {
					if(getCurrentPlayerBoard().getFactory() >= productsList.get(i).getFactory() &&
							getCurrentPlayerBoard().getEngg() >= productsList.get(i).getEngg())
						count++;
				}
				brandValue = (multiplier*numPlayers*count)/divider;

			} else if(qualifier.equalsIgnoreCase("own_both_techs")) {
				ArrayList<String> techCardsName = new ArrayList<String>();
				for(int i = 0; i<techList.size(); i++) {
					techCardsName.add(techList.get(i).getName());
				}
				for(int i = 0; i<productsList.size(); i++) {
					if(techCardsName.contains(productsList.get(i).getTech1name()) && techCardsName.contains(productsList.get(i).getTech2name()))
						count++;
				}
				brandValue = (multiplier*numPlayers*count)/divider;
			}
		}
		return brandValue;
	}

	/**
	 * returns the share of the player in the profit of the product
	 * @param p : product to be built
	 * @param PB  : playerboard for which the profit share has to be calculated
	 * @param totalProfit  : total profit of the product
	 * @return
	 */
	public Integer calculatePlayerShare(Product p,PlayerBoard PB,int totalProfit, PlayerBoard CurrentPlayerBoard,PlayerBoard ownsEngg, PlayerBoard ownsFactory){
		Integer playerShare=0;
		if(getTech(p.getTech1name()).getPlayerBoard() == PB)
			playerShare = playerShare+(totalProfit/4);
		if(getTech(p.getTech2name()).getPlayerBoard() == PB)
			playerShare = playerShare+(totalProfit/4);
		if(ownsEngg!=null && PB == ownsEngg)
			playerShare = playerShare+(totalProfit/4);
		if(ownsFactory!=null && PB == ownsFactory)
			playerShare = playerShare+(totalProfit/4);
		if(PB == CurrentPlayerBoard)
			playerShare = playerShare + (totalProfit%4);
		Log.d(TAG,"playershare " + playerShare);
		return playerShare;
	}
	public int calculate(ArrayList<Tech> techList) {
		int setCount = 0;
		int colorBlue = 0;
		int colorGreen = 0;
		int colorRed = 0;
		int colorYellow = 0;
		int iTmp = 1;
		boolean flag = false;
		for (int i = 0; i<techList.size(); i++) {
			TechnodoColor color = techList.get(i).getColor();
			switch(color.getValue()) {
			case Color.BLUE :
				colorBlue++;
				break;

			case Color.GREEN :
				colorGreen++;
				break;

			case Color.RED :
				colorRed++;
				break;

			case Color.YELLOW :
				colorYellow++;
				break;
			}
		}

		while(!flag) {
			if(colorBlue >= iTmp && colorGreen >= iTmp && colorRed >= iTmp && colorYellow >= iTmp) {
				setCount++;
				iTmp++;
			} else
				flag = true;
		}
		return setCount;	
	}
	/**
	 * Move ahead the Current Player Index, when required
	 * Also triggers the recalculation of turn order on the end of a round
	 * 
	 * @return playerboard of the next player who has the next turn
	 */
	public PlayerBoard calculateNextPlayer() {
		Log.d(TAG,"calculateNextPlayer() called" +nextTurn);

		PlayerBoard currentPlayerboard = null;

		Log.d(TAG," value of next turn is "+nextTurn);
		//			Log.d(TAG," value of currentPlayerboard.getProfile().getName() "+currentPlayerboard.getProfile().getName());

		currentPlayerIndex = currentPlayerIndex+1;

		// Round is over
		if(getCurrentPlayerIndex() >= getNumberOfPlayingPlayers())
		{
			// FIXME: Recalculate turn order based on Descending order of BrandValue
			// Reformulate your turnorder
			currentPlayerIndex = 0;
			PlayerBoard tempPB;


			// sort the playerboards on the basis of their Brand Value
			for(int pass=1; pass < getNumberOfPlayingPlayers(); pass++)
			{
				for (int j = 0; j<getNumberOfPlayingPlayers()-pass;j++){
					if ( playerBoard[j].getBrandValue() > playerBoard[j+1].getBrandValue() )   // change to > for ascending sort
					{
						tempPB = playerBoard[j];                //swap elements
						playerBoard[j] = playerBoard[ j+1 ];
						playerBoard[j+1] = tempPB;
					}

				}}

			Log.d(TAG,"number of sortedPlayerBoard players : "+playerBoard.length);
			Log.d(TAG, "Round Over : Recalculated turnorder + " + turnOrder);

			//initiate next round
			nextRound();
		}

		//update the next player's turn
		nextTurn = getNextTurn(getCurrentPlayerIndex());			
		currentPlayerboard = getCurrentPlayerBoard(nextTurn);

		Log.d(TAG,"calculateNextPlayer() returning" +nextTurn);
		return currentPlayerboard;
	}


	/**
	 * Move to the next round
	 * 
	 * 1. Increment rounds
	 * 2. For each player, make moves left = 2
	 * 3. Increment advertisedRow % 4
	 * 4. Increment subsidyCol % 4
	 * 
	 */
	void nextRound() {
		round = round +1;
		for (PlayerBoard p :playerBoard)
		{
			p.setMovesLeft(2);
		}
		advertisedRows = (advertisedRows + 1)%4;
		subsidy = (subsidy + 1)%4;

	}
	/**
	 * Gives the current Player Board, which is active
	 * @return Current PlayerBoard
	 */
	public PlayerBoard getCurrentPlayerBoard() {
		nextTurn = getNextTurn(getCurrentPlayerIndex());
		Log.d(TAG," value of next turn is "+nextTurn);
		return getCurrentPlayerBoard(nextTurn);

	}

	/**
	 * 
	 * @param nextTurn index of in the playerBoard[]
	 * @return the player who has the next turn
	 */
	public PlayerBoard getCurrentPlayerBoard(int nextTurn) {
		//		Log.d(TAG,""+playerBoard[nextTurn].getProfile().getName());
		return playerBoard[nextTurn];
	}

	public Product getFrontProductPlacingElement(int row, int column){
		return frontProductPlacing[row][column];
	}

	public Product getBackProductPlacingElement(int row, int column){
		return backProductPlacing[row][column];
	}

}

